using System;
using System.Diagnostics;
using System.Collections.Generic;

using org.fourzoas.noise.module;

namespace org.fourzoas.noise.module {
  /// <summary>
  /// A utility class to represent an array of <see cref="Module"/> objects, such as <see cref="Module.m_sourceModule"/>.
  /// Doing it this way allows <see cref="Module"/> to have a <see cref="Module.SourceModules"/> property.
  /// </summary>
  public class ModuleList : IEnumerable<Module> {
    /// <summary>
    /// The data storage for this class.
    /// </summary>
    private List<Module> m_modules;

    /// <summary>
    /// Default constructor.  Does not create the storage.
    /// </summary>
    public ModuleList() {
      m_modules = null;
    }

    /// <summary>
    /// Constructor with initialization.  Allocates requested number of slots.
    /// </summary>
    /// <param name="capacity">Number of modules for which to allocate space.  If &#x2264;0, nothing is done.</param>
    public ModuleList(int capacity) {
      m_modules = null;
      if (capacity > 0) {
        m_modules = new List<Module>(capacity);
        for (int i = 0; i < capacity; i++)
          m_modules.Add(null);
      }
    }

    /// <summary>
    /// Constructor.  Allows creation from an array (or any other collection that implements 
    /// <c><see cref="IEnumerable{Module}"/></c> for <c><see cref="Module"/></c>).
    /// </summary>
    /// <param name="array">A collection of <c><see cref="Module"/></c> objects.  Storage will be initialized
    /// with the contents of this collection.</param>
    public ModuleList(IEnumerable<Module> array) {
      m_modules = new List<Module>(array);
    }

    /// <summary>
    /// Gets or sets a specific <see cref="Module"/> within the list.
    /// </summary>
    /// <param name="index">Which module to access</param>
    /// <returns>The requested module.</returns>
    /// <exception cref="NullReferenceException">Thrown if the list has not been initialized.</exception>
    /// <exception cref="NoModuleException">Thrown if trying to get a <see cref="Module"/> outside the range stored or
    /// if the given <see cref="Module"/> has not been initialized.</exception>
    /// <exception cref="ArgumentOutOfRangeException">Thrown if trying to set a <see cref="Module"/> outside the range.</exception>
    public Module this[int index] {
      get {
        if (m_modules == null)
          throw new NullReferenceException("m_modules not initialized");
        if (index < 0 || index >= m_modules.Count || m_modules[index] == null)
          throw new NoModuleException();
        return m_modules[index];
      }
      set {
        if (m_modules == null)
          throw new NullReferenceException("m_modules not initialized");
        if (index < 0 || index >= m_modules.Count)
          throw new ArgumentOutOfRangeException("index");
        m_modules[index] = value;
      }
    }

    /// <summary>
    /// Destructor.  Just clears the <c><see cref="List{Module}"/></c> storage.
    /// </summary>
    ~ModuleList() {
      if (m_modules != null)
        m_modules.Clear(); // should force contained Modules to be disposed unless otherwise referenced.
    }

    /// <summary>
    /// How many modules are actually stored.
    /// </summary>
    public int Count {
      get { return m_modules.Count; }
    }


    #region IEnumerable<Module> Members

    IEnumerator<Module> IEnumerable<Module>.GetEnumerator() {
      return m_modules.GetEnumerator();
    }

    #endregion

    #region IEnumerable Members

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
      return ((System.Collections.IEnumerable) m_modules).GetEnumerator();
    }

    #endregion
  }
}
